﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Xceed.Silverlight.Data
{
  internal abstract class ConversionEnumerator<TSource, TResult> : AsyncParentEnumerator<TResult>
  {
    #region Static Fields

    private static Guid JumpMoveAndFetchCallId = DataUtils.CreateCallId();

    #endregion

    #region Constructor

    protected ConversionEnumerator( IAsyncEnumerator childEnumerator )
      : base( childEnumerator )
    {
    }

    #endregion

    public override JumpMoveAndFetchAsyncResult BeginJumpMoveAndFetch(
      JumpMoveAndFetchParameters parameters,
      AsyncCallback userCallback,
      object asyncState )
    {
      JumpMoveAndFetchAsyncResult asyncResult = new JumpMoveAndFetchAsyncResult(
                                                  ConversionEnumerator<TSource, TResult>.JumpMoveAndFetchCallId,
                                                  userCallback,
                                                  asyncState,
                                                  parameters );

      JumpMoveAndFetchParameters newParameters = parameters.CreateWithNewSourcePosition(
                                                   this.ConvertSourcePosition(
                                                     parameters.Context,
                                                     parameters.SourcePosition,
                                                     parameters.ExpectedMoveCount,
                                                     parameters.ExpectedFetchCount ) );
      IAsyncEnumerator enumerator = this.ChildEnumerator;

      asyncResult.Start(
        ( cb, st ) => enumerator.BeginJumpMoveAndFetch( newParameters, cb, st ),
        ( par, iar ) => new DependentCallResult( enumerator.EndJumpMoveAndFetch( ( JumpMoveAndFetchAsyncResult )iar ) ) );

      return asyncResult;
    }

    public override JumpMoveAndFetchResult<TResult> EndJumpMoveAndFetch( JumpMoveAndFetchAsyncResult asyncResult )
    {
      asyncResult.ValidateCallEnd( ConversionEnumerator<TSource, TResult>.JumpMoveAndFetchCallId );

      JumpMoveAndFetchResult result = asyncResult.GetCallResult<JumpMoveAndFetchResult>();

      if( ( m_disposed ) || ( result == null ) )
        return null;

      return this.ConvertJumpResult( asyncResult, result );
    }

    protected abstract TResult Convert( DataSourceContext context, TSource value );

    protected abstract TSource ConvertBack( DataSourceContext context, TResult value );

    protected virtual JumpMoveAndFetchResult<TResult> ConvertJumpResult(
      JumpMoveAndFetchAsyncResult asyncResult,
      JumpMoveAndFetchResult childResult )
    {
      DataSourceContext context = asyncResult.Context;
      SourcePosition endPosition = childResult.EndPositionObject;

      if( endPosition is ItemPosition )
      {
        ItemPosition itemPosition = ( ItemPosition )endPosition;
        TSource childEndPosition = ( TSource )itemPosition.Item;

        endPosition = new ItemPosition( this.Convert( context, childEndPosition ) );
      }

      return new JumpMoveAndFetchResult<TResult>(
                   asyncResult,
                   childResult.ActualMoveCount,
                   this.ConvertList( context, childResult.FetchedObjects ),
                   childResult.Result,
                   endPosition,
                   childResult.EndPositionOffset,
                   childResult.TotalCount );
    }

    protected virtual SourcePosition ConvertSourcePosition(
      DataSourceContext context,
      SourcePosition sourcePosition,
      long expectedMoveCount,
      int expectedFetchCount )
    {
      if( sourcePosition is ItemPosition )
      {
        ItemPosition itemPosition = ( ItemPosition )sourcePosition;
        TResult startPosition = ( TResult )itemPosition.Item;

        sourcePosition = new ItemPosition( this.ConvertBack( context, startPosition ) );
      }

      return sourcePosition;
    }

    protected ICollection<TResult> ConvertList( DataSourceContext context, IEnumerable dataList )
    {
      if( dataList == null )
        return null;

      return ( from p in dataList.Cast<TSource>()
               select this.Convert( context, p ) ).ToList();
    }

    protected override IEnumerable<DataSourceChangedEventArgs> TransformDataSourceChangedEventArgs( 
      DataSourceContext context, 
      DataSourceChangedEventArgs e )
    {
      IEnumerable<DataSourceChangedEventArgs> childEventArgs = base.TransformDataSourceChangedEventArgs( context, e );
      List<DataSourceChangedEventArgs> newEventArgs = new List<DataSourceChangedEventArgs>();

      foreach( DataSourceChangedEventArgs eventArgs in childEventArgs )
      {
        if( eventArgs.Action == DataSourceChangedAction.Add )
        {
          newEventArgs.Add(
            new DataSourceChangedEventArgs(
              DataSourceChangedAction.Add,
              this.ConvertList( context, eventArgs.NewItems ).Cast<object>(),
              null ) );
        }
        else if( eventArgs.Action == DataSourceChangedAction.Remove )
        {
          newEventArgs.Add(
            new DataSourceChangedEventArgs(
              DataSourceChangedAction.Remove,
              null,
              this.ConvertList( context, eventArgs.OldItems ).Cast<object>() ) );
        }
        else if( eventArgs.Action == DataSourceChangedAction.Replace )
        {
          newEventArgs.Add(
            new DataSourceChangedEventArgs(
              DataSourceChangedAction.Replace,
              this.ConvertList( context, eventArgs.NewItems ).Cast<object>(),
              this.ConvertList( context, eventArgs.OldItems ).Cast<object>() ) );
        }
        else if( eventArgs.Action == DataSourceChangedAction.Reset )
        {
          newEventArgs.Add( eventArgs );
        }
        else if( eventArgs.Action == DataSourceChangedAction.ItemChanged )
        {
          newEventArgs.Add(
            new DataSourceChangedEventArgs(
              ( from itemChanged in eventArgs.ItemsChanged
                select itemChanged.CreateWithNewItem( this.Convert( context, ( TSource )itemChanged.Item ) ) ).ToList() ) );
        }
        else
        {
          throw new NotSupportedException();
        }
      }

      return newEventArgs;
    }

    protected override void Dispose( bool disposing )
    {
      m_disposed = true;

      base.Dispose( disposing );
    }

    #region Private Fields

    private bool m_disposed; //false

    #endregion
  }
}
